# 交互式反馈 MCP 用户界面
# 开发者：Fábio Ferreira (https://x.com/fabiomlferreira)
# 灵感来源：dotcursorrules.com (https://dotcursorrules.com/)
import os
import sys
import json
import psutil
import argparse
import subprocess
import threading
# import hashlib - 不再需要
from typing import Optional, TypedDict

from PySide6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QLabel, QLineEdit, QPushButton, QCheckBox, QTextEdit, QGroupBox, QSizePolicy
)
from PySide6.QtCore import Qt, Signal, QObject, QTimer, QSettings
from PySide6.QtGui import QTextCursor, QIcon, QKeyEvent, QFont, QFontDatabase, QPalette, QColor

class FeedbackResult(TypedDict):
    """反馈结果数据结构"""
    command_logs: str           # 命令执行日志
    interactive_feedback: str   # 用户交互反馈内容
    auto_continue: bool         # 是否自动继续
    wait_seconds: int          # 等待秒数

class FeedbackConfig(TypedDict):
    """反馈配置数据结构"""
    run_command: str            # 要运行的命令
    execute_automatically: bool # 是否自动执行命令

def set_dark_title_bar(widget: QWidget, dark_title_bar: bool) -> None:
    """
    设置Windows窗口的深色标题栏
    
    Args:
        widget: Qt窗口部件
        dark_title_bar: 是否启用深色标题栏
    """
    # 确保我们在Windows系统上
    if sys.platform != "win32":
        return

    from ctypes import windll, c_uint32, byref

    # 获取Windows构建版本号
    build_number = sys.getwindowsversion().build
    if build_number < 17763:  # 最低要求Windows 10 1809
        return

    # 检查窗口部件的属性是否已经匹配设置
    dark_prop = widget.property("DarkTitleBar")
    if dark_prop is not None and dark_prop == dark_title_bar:
        return

    # 设置属性（dark_title_bar != 0 为True，否则为False）
    widget.setProperty("DarkTitleBar", dark_title_bar)

    # 加载dwmapi.dll并调用DwmSetWindowAttribute
    dwmapi = windll.dwmapi
    hwnd = widget.winId()  # 获取窗口句柄
    attribute = 20 if build_number >= 18985 else 19  # 为较新的构建版本使用较新的属性
    c_dark_title_bar = c_uint32(dark_title_bar)  # 转换为C兼容的uint32类型
    dwmapi.DwmSetWindowAttribute(hwnd, attribute, byref(c_dark_title_bar), 4)

    # 技巧：创建一个1x1像素的无框窗口来强制重绘
    temp_widget = QWidget(None, Qt.FramelessWindowHint)
    temp_widget.resize(1, 1)
    temp_widget.move(widget.pos())
    temp_widget.show()
    temp_widget.deleteLater()  # 在Qt事件循环中安全删除

def get_dark_mode_palette(app: QApplication):
    """
    获取深色模式调色板
    
    Args:
        app: QApplication实例
        
    Returns:
        配置好的深色模式调色板
    """
    darkPalette = app.palette()
    # 设置各种界面元素的颜色
    darkPalette.setColor(QPalette.Window, QColor(53, 53, 53))                    # 窗口背景色
    darkPalette.setColor(QPalette.WindowText, Qt.white)                          # 窗口文本色
    darkPalette.setColor(QPalette.Disabled, QPalette.WindowText, QColor(127, 127, 127))  # 禁用文本色
    darkPalette.setColor(QPalette.Base, QColor(42, 42, 42))                      # 输入框背景色
    darkPalette.setColor(QPalette.AlternateBase, QColor(66, 66, 66))             # 交替背景色
    darkPalette.setColor(QPalette.ToolTipBase, QColor(53, 53, 53))               # 工具提示背景色
    darkPalette.setColor(QPalette.ToolTipText, Qt.white)                         # 工具提示文本色
    darkPalette.setColor(QPalette.Text, Qt.white)                                # 文本色
    darkPalette.setColor(QPalette.Disabled, QPalette.Text, QColor(127, 127, 127))        # 禁用文本色
    darkPalette.setColor(QPalette.Dark, QColor(35, 35, 35))                      # 暗色调
    darkPalette.setColor(QPalette.Shadow, QColor(20, 20, 20))                    # 阴影色
    darkPalette.setColor(QPalette.Button, QColor(53, 53, 53))                    # 按钮背景色
    darkPalette.setColor(QPalette.ButtonText, Qt.white)                          # 按钮文本色
    darkPalette.setColor(QPalette.Disabled, QPalette.ButtonText, QColor(127, 127, 127))  # 禁用按钮文本色
    darkPalette.setColor(QPalette.BrightText, Qt.red)                            # 亮文本色
    darkPalette.setColor(QPalette.Link, QColor(42, 130, 218))                    # 链接色
    darkPalette.setColor(QPalette.Highlight, QColor(42, 130, 218))               # 高亮色
    darkPalette.setColor(QPalette.Disabled, QPalette.Highlight, QColor(80, 80, 80))      # 禁用高亮色
    darkPalette.setColor(QPalette.HighlightedText, Qt.white)                     # 高亮文本色
    darkPalette.setColor(QPalette.Disabled, QPalette.HighlightedText, QColor(127, 127, 127))  # 禁用高亮文本色
    darkPalette.setColor(QPalette.PlaceholderText, QColor(127, 127, 127))        # 占位符文本色
    return darkPalette

def kill_tree(process: subprocess.Popen):
    """
    终止进程及其所有子进程
    
    Args:
        process: 要终止的父进程
    """
    killed: list[psutil.Process] = []
    parent = psutil.Process(process.pid)
    
    # 递归终止所有子进程
    for proc in parent.children(recursive=True):
        try:
            proc.kill()
            killed.append(proc)
        except psutil.Error:
            pass
    
    # 终止父进程
    try:
        parent.kill()
    except psutil.Error:
        pass
    killed.append(parent)

    # 终止任何剩余的进程
    for proc in killed:
        try:
            if proc.is_running():
                proc.terminate()
        except psutil.Error:
            pass

def get_user_environment() -> dict[str, str]:
    """
    获取用户环境变量
    
    在Windows系统上使用API获取用户环境变量，在其他系统上直接返回os.environ
    
    Returns:
        包含环境变量的字典
    """
    if sys.platform != "win32":
        return os.environ.copy()

    import ctypes
    from ctypes import wintypes

    # 加载所需的DLL
    advapi32 = ctypes.WinDLL("advapi32")
    userenv = ctypes.WinDLL("userenv")
    kernel32 = ctypes.WinDLL("kernel32")

    # 常量定义
    TOKEN_QUERY = 0x0008

    # 函数原型定义
    OpenProcessToken = advapi32.OpenProcessToken
    OpenProcessToken.argtypes = [wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE)]
    OpenProcessToken.restype = wintypes.BOOL

    CreateEnvironmentBlock = userenv.CreateEnvironmentBlock
    CreateEnvironmentBlock.argtypes = [ctypes.POINTER(ctypes.c_void_p), wintypes.HANDLE, wintypes.BOOL]
    CreateEnvironmentBlock.restype = wintypes.BOOL

    DestroyEnvironmentBlock = userenv.DestroyEnvironmentBlock
    DestroyEnvironmentBlock.argtypes = [wintypes.LPVOID]
    DestroyEnvironmentBlock.restype = wintypes.BOOL

    GetCurrentProcess = kernel32.GetCurrentProcess
    GetCurrentProcess.argtypes = []
    GetCurrentProcess.restype = wintypes.HANDLE

    CloseHandle = kernel32.CloseHandle
    CloseHandle.argtypes = [wintypes.HANDLE]
    CloseHandle.restype = wintypes.BOOL

    # 获取进程令牌
    token = wintypes.HANDLE()
    if not OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, ctypes.byref(token)):
        raise RuntimeError("无法打开进程令牌")

    try:
        # 创建环境变量块
        environment = ctypes.c_void_p()
        if not CreateEnvironmentBlock(ctypes.byref(environment), token, False):
            raise RuntimeError("无法创建环境变量块")

        try:
            # 将环境变量块转换为字符串列表
            result = {}
            env_ptr = ctypes.cast(environment, ctypes.POINTER(ctypes.c_wchar))
            offset = 0

            while True:
                # 获取当前偏移位置的字符串
                current_string = ""
                while env_ptr[offset] != "\0":
                    current_string += env_ptr[offset]
                    offset += 1

                # 跳过空终止符
                offset += 1

                # 如果遇到双空终止符则退出
                if not current_string:
                    break

                equal_index = current_string.index("=")
                if equal_index == -1:
                    continue

                key = current_string[:equal_index]
                value = current_string[equal_index + 1:]
                result[key] = value

            return result

        finally:
            DestroyEnvironmentBlock(environment)

    finally:
        CloseHandle(token)

class FeedbackTextEdit(QTextEdit):
    """
    自定义的文本编辑器，支持Ctrl+Enter快捷键提交反馈
    """
    def __init__(self, parent=None):
        super().__init__(parent)

    def keyPressEvent(self, event: QKeyEvent):
        """处理键盘事件，特别是Ctrl+Enter组合键"""
        if event.key() == Qt.Key_Return and event.modifiers() == Qt.ControlModifier:
            # 查找父级FeedbackUI实例并调用提交方法
            parent = self.parent()
            while parent and not isinstance(parent, FeedbackUI):
                parent = parent.parent()
            if parent:
                parent._submit_feedback()
        else:
            super().keyPressEvent(event)

class LogSignals(QObject):
    """
    日志信号类，用于线程间的日志传递
    """
    append_log = Signal(str)  # 添加日志信号

class FeedbackUI(QMainWindow):
    """
    反馈界面主窗口类
    
    提供命令执行、日志查看和用户反馈收集的图形界面
    """
    def __init__(self, project_directory: str, prompt: str):
        """
        初始化反馈界面
        
        Args:
            project_directory: 项目目录路径
            prompt: 显示给用户的提示信息
        """
        super().__init__()
        self.project_directory = project_directory
        self.prompt = prompt

        # 进程相关变量
        self.process: Optional[subprocess.Popen] = None
        self.log_buffer = []            # 日志缓冲区
        self.feedback_result = None     # 反馈结果
        self.log_signals = LogSignals() # 日志信号对象
        self.log_signals.append_log.connect(self._append_log)
        
        # 倒计时相关变量
        self.countdown_timer = QTimer()
        self.countdown_timer.timeout.connect(self._update_countdown)
        self.countdown_seconds = 0
        self.is_auto_submit = False  # 标记是否是自动提交

        # 设置窗口属性
        self.setWindowTitle("交互式反馈 MCP")
        script_dir = os.path.dirname(os.path.abspath(__file__))
        icon_path = os.path.join(script_dir, "images", "feedback.png")
        self.setWindowIcon(QIcon(icon_path))
        self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)  # 窗口置顶
        
        # 配置文件管理
        # 使用feedback_ui.py文件同目录下的配置文件
        config_file_path = os.path.join(script_dir, "feedback_config.ini")
        self.settings = QSettings(config_file_path, QSettings.IniFormat)
        
        # 加载窗口尺寸和位置
        if not self.settings.contains("UI/geometry"):
            # 如果没有保存的窗口尺寸，设置默认值
            self.resize(800, 600)
            screen = QApplication.primaryScreen().geometry()
            x = (screen.width() - 800) // 2
            y = (screen.height() - 600) // 2
            self.move(x, y)
        
        # 加载UI配置
        self.settings.beginGroup("UI")
        geometry = self.settings.value("geometry")
        if geometry:
            self.restoreGeometry(geometry)
        state = self.settings.value("windowState")
        if state:
            self.restoreState(state)
        command_section_visible = self.settings.value("commandSectionVisible", False, type=bool)
        self.settings.endGroup()
        
        # 加载命令配置
        self.settings.beginGroup("Command")
        loaded_run_command = self.settings.value("run_command", "", type=str)
        loaded_execute_auto = self.settings.value("execute_automatically", False, type=bool)
        self.settings.endGroup()
        
        # 加载反馈配置
        self.settings.beginGroup("Feedback")
        self.loaded_auto_continue = self.settings.value("auto_continue", False, type=bool)
        self.loaded_wait_seconds = self.settings.value("wait_seconds", 10, type=int)
        self.loaded_feedback_text = self.settings.value("feedback_text", "", type=str)
        self.loaded_auto_rounds = self.settings.value("auto_rounds", 5, type=int)
        self.settings.endGroup()
        
        # 创建配置对象
        self.config: FeedbackConfig = {
            "run_command": loaded_run_command,
            "execute_automatically": loaded_execute_auto
        }

        # 创建用户界面（配置在这里被使用来设置初始值）
        self._create_ui()

        # 设置命令区域可见性（必须在_create_ui之后执行，因为需要相关部件已创建）
        self.command_group.setVisible(not command_section_visible)
        if not command_section_visible:
            self.toggle_command_button.setText("显示命令区域")
        else:
            self.toggle_command_button.setText("隐藏命令区域")

        # 应用深色标题栏
        set_dark_title_bar(self, True)

        # 如果配置了自动执行命令且命令不为空，则执行
        if self.config.get("execute_automatically", False) and self.config.get("run_command", "").strip():
            self._run_command()
            
        # 启动倒计时功能 - 确保在UI创建完成后进行
        if self.loaded_auto_continue:
            try:
                # 确保倒计时秒数是正数
                if self.loaded_wait_seconds > 0:
                    self.countdown_seconds = self.loaded_wait_seconds
                    # 确保界面上显示正确的秒数
                    self.wait_seconds.setText(str(self.countdown_seconds))
                    # 延迟启动倒计时，确保UI已完全加载
                    QTimer.singleShot(500, self._start_countdown)
            except (ValueError, TypeError):
                # 如果转换失败，不启动倒计时
                pass

    def _format_windows_path(self, path: str) -> str:
        """
        格式化Windows路径显示
        
        Args:
            path: 原始路径
            
        Returns:
            格式化后的路径（Windows风格）
        """
        if sys.platform == "win32":
            # 将正斜杠转换为反斜杠
            path = path.replace("/", "\\")
            # 如果路径以x:\开头，将驱动器字母大写
            if len(path) >= 2 and path[1] == ":" and path[0].isalpha():
                path = path[0].upper() + path[1:]
        return path

    def _create_ui(self):
        """
        创建用户界面
        
        构建包含命令区域、控制台和反馈区域的完整界面
        """
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)

        # 命令区域切换按钮
        self.toggle_command_button = QPushButton("显示命令区域")
        self.toggle_command_button.clicked.connect(self._toggle_command_section)
        layout.addWidget(self.toggle_command_button)

        # 命令区域
        self.command_group = QGroupBox("命令")
        command_layout = QVBoxLayout(self.command_group)

        # 工作目录标签
        formatted_path = self._format_windows_path(self.project_directory)
        working_dir_label = QLabel(f"工作目录: {formatted_path}")
        command_layout.addWidget(working_dir_label)

        # 命令输入行
        command_input_layout = QHBoxLayout()
        self.command_entry = QLineEdit()
        self.command_entry.setText(self.config["run_command"])
        self.command_entry.returnPressed.connect(self._run_command)
        self.command_entry.textChanged.connect(self._update_config)
        self.run_button = QPushButton("运行(&R)")
        self.run_button.clicked.connect(self._run_command)

        command_input_layout.addWidget(self.command_entry)
        command_input_layout.addWidget(self.run_button)
        command_layout.addLayout(command_input_layout)

        # 自动执行和保存配置行
        auto_layout = QHBoxLayout()
        self.auto_check = QCheckBox("下次运行时自动执行")
        self.auto_check.setChecked(self.config.get("execute_automatically", False))
        self.auto_check.stateChanged.connect(self._update_config)

        save_button = QPushButton("保存配置(&S)")
        save_button.clicked.connect(self._save_config)

        auto_layout.addWidget(self.auto_check)
        auto_layout.addStretch()
        auto_layout.addWidget(save_button)
        command_layout.addLayout(auto_layout)

        # 控制台区域（现在是命令分组的一部分）
        console_group = QGroupBox("控制台")
        console_layout_internal = QVBoxLayout(console_group)
        console_group.setMinimumHeight(200)

        # 日志文本区域
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        font = QFont(QFontDatabase.systemFont(QFontDatabase.FixedFont))
        font.setPointSize(9)
        self.log_text.setFont(font)
        console_layout_internal.addWidget(self.log_text)

        # 清除按钮
        button_layout = QHBoxLayout()
        self.clear_button = QPushButton("清除(&C)")
        self.clear_button.clicked.connect(self.clear_logs)
        button_layout.addStretch()
        button_layout.addWidget(self.clear_button)
        console_layout_internal.addLayout(button_layout)
        
        command_layout.addWidget(console_group)

        self.command_group.setVisible(False) 
        layout.addWidget(self.command_group)

        # 反馈区域（调整高度）
        self.feedback_group = QGroupBox("反馈")
        feedback_layout = QVBoxLayout(self.feedback_group)

        # 简短描述标签（来自self.prompt）
        self.description_label = QLabel(self.prompt)
        self.description_label.setWordWrap(True)
        feedback_layout.addWidget(self.description_label)

        self.feedback_text = FeedbackTextEdit()
        font_metrics = self.feedback_text.fontMetrics()
        row_height = font_metrics.height()
        # 计算5行的高度加上边距的一些填充
        padding = self.feedback_text.contentsMargins().top() + self.feedback_text.contentsMargins().bottom() + 5 # 额外的垂直填充
        self.feedback_text.setMinimumHeight(5 * row_height + padding)

        self.feedback_text.setPlaceholderText("在此处输入您的反馈 (Ctrl+Enter 提交)")
        
        # 根据auto_continue状态决定是否设置已保存的反馈文本
        if self.loaded_auto_continue and self.loaded_feedback_text:
            self.feedback_text.setText(self.loaded_feedback_text)
        submit_button = QPushButton("发送反馈 (Ctrl+Enter)(&S)")
        submit_button.clicked.connect(self._submit_feedback)
        
        # 设置按钮宽度刚好容纳文字并左右留出5px的间距
        submit_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        font_metrics = submit_button.fontMetrics()
        text_width = font_metrics.horizontalAdvance(submit_button.text())
        submit_button.setFixedWidth(text_width + 10)  # 左右各留出5px的间距
        # submit_button.setStyleSheet("qproperty-alignment: AlignCenter;")  # 文字居中 - 注释掉，这个属性不存在
        
        # 创建一个水平布局用于添加自动继续勾选框、等待秒数编辑框和提交按钮
        button_layout = QHBoxLayout()
        
        # 添加自动继续勾选框
        self.auto_continue_check = QCheckBox("自动发送")
        self.auto_continue_check.setChecked(self.loaded_auto_continue)
        # 连接状态变化事件，当选中状态改变时处理
        self.auto_continue_check.stateChanged.connect(self._handle_auto_continue_changed)
        button_layout.addWidget(self.auto_continue_check)
        
        # 添加等待秒数编辑框和标签
        wait_label = QLabel("等待")
        button_layout.addWidget(wait_label)
        
        self.wait_seconds = QLineEdit()
        self.wait_seconds.setText(str(self.loaded_wait_seconds))
        self.wait_seconds.setFixedWidth(40)  # 设置一个合适的宽度
        # 连接文本变化事件
        self.wait_seconds.textChanged.connect(self._handle_wait_seconds_changed)
        button_layout.addWidget(self.wait_seconds)
        
        second_label = QLabel("秒")
        button_layout.addWidget(second_label)
        
        # 添加自动轮数编辑框和标签
        auto_label = QLabel("自动")
        button_layout.addWidget(auto_label)
        
        self.auto_rounds = QLineEdit()
        self.auto_rounds.setText(str(self.loaded_auto_rounds))
        self.auto_rounds.setFixedWidth(40)  # 设置一个合适的宽度
        # 连接文本变化事件
        self.auto_rounds.textChanged.connect(self._handle_auto_rounds_changed)
        button_layout.addWidget(self.auto_rounds)
        
        rounds_label = QLabel("轮")
        button_layout.addWidget(rounds_label)
        
        # 添加弹簧和提交按钮
        button_layout.addStretch()
        button_layout.addWidget(submit_button)
        button_layout.setContentsMargins(0, 0, 0, 0)

        feedback_layout.addWidget(self.feedback_text)
        feedback_layout.addLayout(button_layout)  # 添加按钮布局而不是直接添加按钮

        # 为反馈组设置最小高度以容纳其内容
        # 这将基于描述标签和5行反馈文本
        self.feedback_group.setMinimumHeight(self.description_label.sizeHint().height() + self.feedback_text.minimumHeight() + submit_button.sizeHint().height() + feedback_layout.spacing() * 3 + feedback_layout.contentsMargins().top() + feedback_layout.contentsMargins().bottom() + 10) # 额外填充

        # 按特定顺序添加小部件
        layout.addWidget(self.feedback_group)

        # 制作人员/联系方式标签
        contact_label = QLabel('需要改进？联系 Fábio Ferreira <a href="https://x.com/fabiomlferreira">X.com</a> 或访问 <a href="https://dotcursorrules.com/">dotcursorrules.com</a>')
        contact_label.setOpenExternalLinks(True)
        contact_label.setAlignment(Qt.AlignCenter)
        # 可选择让字体稍小一些，不那么突出
        contact_label.setStyleSheet("font-size: 9pt; color: #cccccc;") # 深色主题的浅灰色
        layout.addWidget(contact_label)

    def _toggle_command_section(self):
        """切换命令区域的可见性并保存状态"""
        is_visible = self.command_group.isVisible()
        self.command_group.setVisible(not is_visible)
        
        # 更新按钮文本
        if not is_visible:
            self.toggle_command_button.setText("隐藏命令区域")
        else:
            self.toggle_command_button.setText("显示命令区域")
            
        # 保存命令区域可见性状态
        self.settings.beginGroup("UI")
        self.settings.setValue("commandSectionVisible", is_visible)  # 保存切换前的状态
        self.settings.endGroup()

    def _update_config(self):
        """更新配置并保存到文件"""
        self.config["run_command"] = self.command_entry.text()
        self.config["execute_automatically"] = self.auto_check.isChecked()
        
        # 立即保存命令配置
        self.settings.beginGroup("Command")
        self.settings.setValue("run_command", self.config["run_command"])
        self.settings.setValue("execute_automatically", self.config["execute_automatically"])
        self.settings.endGroup()

    def _append_log(self, text: str):
        """
        添加日志文本到缓冲区和显示区域
        
        Args:
            text: 要添加的日志文本
        """
        self.log_buffer.append(text)
        self.log_text.append(text.rstrip())
        cursor = self.log_text.textCursor()
        cursor.movePosition(QTextCursor.End)
        self.log_text.setTextCursor(cursor)

    def _check_process_status(self):
        """
        检查进程状态，如果进程已结束则进行清理
        """
        if self.process and self.process.poll() is not None:
            # 进程已终止
            exit_code = self.process.poll()
            self._append_log(f"\n进程已退出，退出代码 {exit_code}\n")
            self.run_button.setText("运行(&R)")
            self.process = None
            self.activateWindow()
            self.feedback_text.setFocus()

    def _run_command(self):
        """
        运行或停止命令
        
        如果当前有进程在运行，则停止它；否则启动新进程执行用户输入的命令
        """
        if self.process:
            kill_tree(self.process)
            self.process = None
            self.run_button.setText("运行(&R)")
            return

        # 清除日志缓冲区但保持UI日志可见
        self.log_buffer = []

        command = self.command_entry.text()
        if not command:
            self._append_log("请输入要运行的命令\n")
            return

        self._append_log(f"$ {command}\n")
        self.run_button.setText("停止(&p)")

        try:
            self.process = subprocess.Popen(
                command,
                shell=True,                     # 使用shell执行
                cwd=self.project_directory,     # 工作目录
                stdout=subprocess.PIPE,         # 捕获标准输出
                stderr=subprocess.PIPE,         # 捕获标准错误
                env=get_user_environment(),     # 使用用户环境
                text=True,                      # 文本模式
                bufsize=1,                      # 行缓冲
                encoding="utf-8",               # UTF-8编码
                errors="ignore",                # 忽略编码错误
                close_fds=True,                 # 关闭文件描述符
            )

            def read_output(pipe):
                """读取进程输出的线程函数"""
                for line in iter(pipe.readline, ""):
                    self.log_signals.append_log.emit(line)

            # 启动线程读取标准输出
            threading.Thread(
                target=read_output,
                args=(self.process.stdout,),
                daemon=True
            ).start()

            # 启动线程读取标准错误
            threading.Thread(
                target=read_output,
                args=(self.process.stderr,),
                daemon=True
            ).start()

            # 启动进程状态检查
            self.status_timer = QTimer()
            self.status_timer.timeout.connect(self._check_process_status)
            self.status_timer.start(100)  # 每100毫秒检查一次

        except Exception as e:
            self._append_log(f"运行命令时出错: {str(e)}\n")
            self.run_button.setText("运行(&R)")

    def _submit_feedback(self):
        """
        提交用户反馈并关闭窗口
        
        收集用户输入的反馈内容、倒计时设置等，保存到配置文件，然后关闭窗口
        """
        # 尝试将等待秒数转换为整数，如果转换失败则默认为10
        try:
            wait_seconds = int(self.wait_seconds.text().strip())
        except ValueError:
            wait_seconds = 10
        
        # 处理自动轮数逻辑
        try:
            auto_rounds = int(self.auto_rounds.text().strip()) if self.auto_rounds.text().strip() else 0
        except ValueError:
            auto_rounds = 0
            
        # 如果是自动提交且有剩余轮数，减去1轮
        if self.is_auto_submit and self.auto_continue_check.isChecked() and auto_rounds > 0:
            auto_rounds -= 1
            if auto_rounds <= 0:
                # 轮数用完，清空输入框并取消自动发送
                self.auto_rounds.blockSignals(True)
                self.auto_rounds.setText("")
                self.auto_rounds.blockSignals(False)
                self.auto_continue_check.setChecked(False)
                auto_rounds = 0
            else:
                # 更新剩余轮数
                self.auto_rounds.blockSignals(True)
                self.auto_rounds.setText(str(auto_rounds))
                self.auto_rounds.blockSignals(False)
        
        # 重置自动提交标记
        self.is_auto_submit = False
        
        # 保存反馈相关设置
        self.settings.beginGroup("Feedback")
        self.settings.setValue("auto_continue", self.auto_continue_check.isChecked())
        self.settings.setValue("wait_seconds", wait_seconds)
        self.settings.setValue("feedback_text", self.feedback_text.toPlainText())
        self.settings.setValue("auto_rounds", auto_rounds)
        self.settings.endGroup()
            
        self.feedback_result = FeedbackResult(
            command_logs="".join(self.log_buffer),
            interactive_feedback=self.feedback_text.toPlainText().strip(),
            auto_continue=self.auto_continue_check.isChecked(),
            wait_seconds=wait_seconds
        )
        self.close()

    def clear_logs(self):
        """清除日志缓冲区和显示区域"""
        self.log_buffer = []
        self.log_text.clear()

    def _save_config(self):
        """
        保存所有配置到文件
        
        包括命令配置、UI配置和反馈配置
        """
        # 保存命令配置
        self.settings.beginGroup("Command")
        self.settings.setValue("run_command", self.config["run_command"])
        self.settings.setValue("execute_automatically", self.config["execute_automatically"])
        self.settings.endGroup()
        
        # 保存UI配置
        self.settings.beginGroup("UI")
        self.settings.setValue("geometry", self.saveGeometry())
        self.settings.setValue("windowState", self.saveState())
        self.settings.setValue("commandSectionVisible", not self.command_group.isVisible())
        self.settings.endGroup()
        
        # 保存反馈设置
        self.settings.beginGroup("Feedback")
        self.settings.setValue("auto_continue", self.auto_continue_check.isChecked())
        self.settings.setValue("wait_seconds", self.wait_seconds.text())
        self.settings.setValue("feedback_text", self.feedback_text.toPlainText())
        self.settings.setValue("auto_rounds", self.auto_rounds.text())
        self.settings.endGroup()
        
        self._append_log("已保存此项目的配置。\n")

    def closeEvent(self, event):
        """
        窗口关闭事件处理
        
        在窗口关闭前保存配置并清理进程
        """
        # 关闭前保存窗口状态和设置
        self._save_config()
        
        if self.process:
            kill_tree(self.process)
        super().closeEvent(event)

    def run(self) -> FeedbackResult:
        """
        显示界面并等待用户输入
        
        Returns:
            用户反馈结果
        """
        self.show()
        QApplication.instance().exec()

        if self.process:
            kill_tree(self.process)

        if not self.feedback_result:
            return FeedbackResult(command_logs="".join(self.log_buffer), interactive_feedback="", auto_continue=False, wait_seconds=10)

        return self.feedback_result

    def _update_countdown(self):
        """每秒更新倒计时，当倒计时结束时自动提交反馈"""
        self.countdown_seconds -= 1
        # 临时断开信号连接，避免触发文本变化事件
        self.wait_seconds.blockSignals(True)
        self.wait_seconds.setText(str(self.countdown_seconds))
        self.wait_seconds.blockSignals(False)
        
        if self.countdown_seconds <= 0:
            # 停止计时器
            self.countdown_timer.stop()
            # 提交前恢复秒数（也要阻止信号）
            self.wait_seconds.blockSignals(True)
            self.wait_seconds.setText(str(self.loaded_wait_seconds))
            self.wait_seconds.blockSignals(False)
            # 标记为自动提交并提交
            self.is_auto_submit = True
            self._submit_feedback()

    def _start_countdown(self):
        """开始倒计时"""
        try:
            # 停止已有的计时器
            if self.countdown_timer.isActive():
                self.countdown_timer.stop()
                # 取消倒计时恢复秒数（阻止信号）
                self.wait_seconds.blockSignals(True)
                self.wait_seconds.setText(str(self.loaded_wait_seconds))
                self.wait_seconds.blockSignals(False)
            
            # 获取等待秒数
            self.countdown_seconds = int(self.wait_seconds.text().strip())
            # 确保界面显示正确的秒数（阻止信号）
            self.wait_seconds.blockSignals(True)
            self.wait_seconds.setText(str(self.countdown_seconds))
            self.wait_seconds.blockSignals(False)
            
            if self.countdown_seconds <= 0:
                # 提交前恢复秒数（阻止信号）
                self.wait_seconds.blockSignals(True)
                self.wait_seconds.setText(str(self.loaded_wait_seconds))
                self.wait_seconds.blockSignals(False)
                # 如果秒数小于等于0，直接提交
                self._submit_feedback()
                return
                
            # 启动计时器
            self.countdown_timer.start(1000)  # 每秒更新一次
        except ValueError:
            # 如果无法转换为整数，不启动倒计时
            pass

    def _handle_auto_continue_changed(self, state):
        """处理自动继续选中状态变化"""
        # 保存设置
        self.settings.beginGroup("Feedback")
        self.settings.setValue("auto_continue", state == Qt.Checked)
        self.settings.endGroup()
        
        if state == Qt.Checked:
            # 如果选中，则开始倒计时
            self._start_countdown()
        else:
            # 如果取消选中，则停止倒计时
            if self.countdown_timer.isActive():
                self.countdown_timer.stop()
                # 取消倒计时恢复秒数
                self.wait_seconds.setText(str(self.loaded_wait_seconds))
    
    def _handle_wait_seconds_changed(self, text):
        """处理等待秒数文本变化"""
        # 尝试保存设置
        try:
            wait_seconds = int(text.strip())
            self.settings.beginGroup("Feedback")
            self.settings.setValue("wait_seconds", wait_seconds)
            self.settings.endGroup()
        except ValueError:
            pass
            
        # 注意：不在文本改变时重新启动计时器，避免无限循环
        # 用户可以通过手动切换auto_continue复选框来重新启动计时器

    def _handle_auto_rounds_changed(self, text):
        """处理自动轮数文本变化"""
        # 尝试保存设置
        try:
            auto_rounds = int(text.strip())
            self.settings.beginGroup("Feedback")
            self.settings.setValue("auto_rounds", auto_rounds)
            self.settings.endGroup()
        except ValueError:
            pass

# 注释：此处的函数已删除，因为配置现在存储在项目目录下的文件中

def feedback_ui(project_directory: str, prompt: str, output_file: Optional[str] = None) -> Optional[FeedbackResult]:
    """
    创建并运行反馈界面
    
    Args:
        project_directory: 项目目录路径
        prompt: 显示给用户的提示信息
        output_file: 可选的输出文件路径，如果提供则将结果保存到文件
        
    Returns:
        反馈结果，如果指定了output_file则返回None
    """
    app = QApplication.instance() or QApplication()
    app.setPalette(get_dark_mode_palette(app))   # 应用深色主题
    app.setStyle("Fusion")                       # 使用Fusion样式
    ui = FeedbackUI(project_directory, prompt)
    result = ui.run()

    if output_file and result:
        # 确保目录存在
        os.makedirs(os.path.dirname(output_file) if os.path.dirname(output_file) else ".", exist_ok=True)
        # 将结果保存到输出文件
        with open(output_file, "w", encoding='utf-8') as f:
            json.dump(result, f, ensure_ascii=False, indent=2)
        return None

    return result

if __name__ == "__main__":
    # 命令行参数解析
    parser = argparse.ArgumentParser(description="运行反馈界面")
    parser.add_argument("--project-directory", default=os.getcwd(), help="运行命令的项目目录")
    parser.add_argument("--prompt", default="我已实现您请求的更改。", help="显示给用户的提示信息")
    parser.add_argument("--output-file", help="保存反馈结果的JSON文件路径")
    args = parser.parse_args()

    result = feedback_ui(args.project_directory, args.prompt, args.output_file)
    if result:
        print(f"\n收集的日志: \n{result['command_logs']}")
        print(f"\n收到的反馈:\n{result['interactive_feedback']}")
    sys.exit(0)
